/**
 * Copyright (C) 2010 - 2013 Harry Glasgow
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.googlecode.jaden.ui.dialog.automatedtraining;

import com.googlecode.jaden.common.config.ProjectConfiguration;
import com.googlecode.jaden.common.other.JadenException;
import com.googlecode.jaden.common.other.Utils;
import com.googlecode.jaden.ui.Constants;
import com.googlecode.jaden.ui.UI;
import com.googlecode.jaden.ui.UiUtils;
import com.googlecode.jaden.ui.dialog.BaseDialog;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.prefs.Preferences;

public class AutomatedTrainingConfigDialog extends BaseDialog {

    private final UI ui;
    private final JComboBox<TrainingMode> trainingModeComboBox;
    private final SpinnerNumberModel roundsSpinnerModel;
    private final SpinnerNumberModel jogsSpinnerModel;
    private final SpinnerNumberModel minLayersSpinnerModel;
    private final SpinnerNumberModel maxLayersSpinnerModel;
    private final SpinnerNumberModel momentumSpinnerModel;
    private final SpinnerNumberModel learningRateSpinnerModel;
    private final SpinnerNumberModel epochsSpinnerModel;

    private int numberOfInputs;

    public AutomatedTrainingConfigDialog(UI ui, ProjectConfiguration projectConfiguration, Preferences preferences) {
        super(ui, Constants.DIALOG_AUTOMATED_TRAINING_TITLE);
        this.ui = ui;

        numberOfInputs = projectConfiguration.getInputConfigurations().length;

        DefaultComboBoxModel<TrainingMode> trainingModeComboBoxModel = new DefaultComboBoxModel<TrainingMode>();
        trainingModeComboBox = new JComboBox<TrainingMode>(trainingModeComboBoxModel);
        trainingModeComboBoxModel.addElement(TrainingMode.Forward);
        trainingModeComboBoxModel.addElement(TrainingMode.Backward);
        trainingModeComboBoxModel.addElement(TrainingMode.Exhaustive);
        trainingModeComboBoxModel.addElement(TrainingMode.None);
        JLabel trainingModeLabel = new JLabel(Utils.translate(Constants.LABEL_TRAINING_MODE_TEXT));

        roundsSpinnerModel = new SpinnerNumberModel(1, 1, 99, 1);
        JSpinner roundsSpinner = new JSpinner(roundsSpinnerModel);
        JLabel roundsLabel = new JLabel(Utils.translate(Constants.LABEL_ROUNDS_TEXT));

        jogsSpinnerModel = new SpinnerNumberModel(0, 0, 99, 1);
        JSpinner jogsSpinner = new JSpinner(jogsSpinnerModel);
        JLabel jogsLabel = new JLabel(Utils.translate(Constants.LABEL_JOGS_TEXT));

        learningRateSpinnerModel = new SpinnerNumberModel(0.1, 0.1, 1, 0.1);
        JSpinner learningRateSpinner = new JSpinner(learningRateSpinnerModel);
        JLabel learningRateLabel = new JLabel(Utils.translate(Constants.LABEL_LEARNING_RATE_TEXT));

        minLayersSpinnerModel = new SpinnerNumberModel(2, 1, 5, 1);
        JSpinner minLayersSpinner = new JSpinner(minLayersSpinnerModel);
        JLabel minLayersLabel = new JLabel(Utils.translate(Constants.LABEL_MINIMUM_LAYERS_TEXT));

        maxLayersSpinnerModel = new SpinnerNumberModel(3, 1, 5, 1);
        JSpinner maxLayersSpinner = new JSpinner(maxLayersSpinnerModel);
        JLabel maxLayersLabel = new JLabel(Utils.translate(Constants.LABEL_MAXIMUM_LAYERS_TEXT));

        momentumSpinnerModel = new SpinnerNumberModel(0.0, 0.0, 1.0, 0.1);
        JSpinner momentumSpinner = new JSpinner(momentumSpinnerModel);
        JLabel momentumLabel = new JLabel(Utils.translate(Constants.LABEL_MOMENTUM_TEXT));

        epochsSpinnerModel = new SpinnerNumberModel(1000, 1, 9999, 1);
        JSpinner epochsSpinner = new JSpinner(epochsSpinnerModel);
        JLabel epochsLabel = new JLabel(Utils.translate(Constants.LABEL_EPOCHS_TEXT));

        // Hack. The momentum spinner does not display enough width if its
        // original value is zero. So force it to be the same size as
        // the learning rate spinner.
        UiUtils.enWidth(learningRateSpinner, momentumSpinner);

        UiUtils.enWidth(trainingModeLabel, minLayersLabel, maxLayersLabel, roundsLabel, jogsLabel, learningRateLabel,
        momentumLabel, epochsLabel);

        JPanel mainPanel = UiUtils.verticalList(
                UiUtils.horizontalList(trainingModeLabel, trainingModeComboBox),
                UiUtils.horizontalList(minLayersLabel, minLayersSpinner),
                UiUtils.horizontalList(maxLayersLabel, maxLayersSpinner),
                UiUtils.horizontalList(roundsLabel, roundsSpinner),
                UiUtils.horizontalList(jogsLabel, jogsSpinner),
                UiUtils.horizontalList(learningRateLabel, learningRateSpinner),
                UiUtils.horizontalList(momentumLabel, momentumSpinner),
                UiUtils.horizontalList(epochsLabel, epochsSpinner)
        );

        trainingModeComboBox.setSelectedItem(projectConfiguration.getTrainingMode());
        roundsSpinnerModel.setValue(projectConfiguration.getRounds());
        jogsSpinnerModel.setValue(projectConfiguration.getJogs());
        learningRateSpinnerModel.setValue(projectConfiguration.getAutomatedLearningRate());
        momentumSpinnerModel.setValue(projectConfiguration.getAutomatedMomentum());
        epochsSpinnerModel.setValue(projectConfiguration.getAutomatedEpochs());
        minLayersSpinnerModel.setValue(projectConfiguration.getMinimumLayers());
        maxLayersSpinnerModel.setValue(projectConfiguration.getMaximumLayers());

        minLayersSpinner.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                if (minLayersSpinnerModel.getNumber().intValue() > maxLayersSpinnerModel.getNumber().intValue()) {
                    minLayersSpinnerModel.setValue(minLayersSpinnerModel.getNumber().intValue() - 1);
                }
            }
        });

        maxLayersSpinner.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                if (minLayersSpinnerModel.getNumber().intValue() > maxLayersSpinnerModel.getNumber().intValue()) {
                    maxLayersSpinnerModel.setValue(maxLayersSpinnerModel.getNumber().intValue() + 1);
                }
            }
        });

        setMainPanel(mainPanel, preferences);

        JButton button = new JButton(Utils.translate(Constants.LABEL_TRAIN_TEXT));
        addButton(button);
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                train();
            }
        });

    }

    private void train() {
        if ((trainingModeComboBox.getSelectedItem()) == TrainingMode.Exhaustive && numberOfInputs > 63) {
            JOptionPane.showMessageDialog(this,
                    Utils.translate(Constants.MESSAGE_TOO_MANY_INPUTS, String.valueOf(numberOfInputs)));
            return;
        }

        ui.updateAutomatedTrainingConfig((TrainingMode) trainingModeComboBox.getSelectedItem(),
                roundsSpinnerModel.getNumber().intValue(),
                jogsSpinnerModel.getNumber().intValue(),
                learningRateSpinnerModel.getNumber().doubleValue(),
                momentumSpinnerModel.getNumber().doubleValue(),
                epochsSpinnerModel.getNumber().intValue(),
                minLayersSpinnerModel.getNumber().intValue(),
                maxLayersSpinnerModel.getNumber().intValue());
        dispose();
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                ui.runAutomatedTraining();
            }
        });
    }

    @Override
    protected void doOk() throws JadenException {
        ui.updateAutomatedTrainingConfig((TrainingMode) trainingModeComboBox.getSelectedItem(),
                roundsSpinnerModel.getNumber().intValue(),
                jogsSpinnerModel.getNumber().intValue(),
                learningRateSpinnerModel.getNumber().doubleValue(),
                momentumSpinnerModel.getNumber().doubleValue(),
                epochsSpinnerModel.getNumber().intValue(),
                minLayersSpinnerModel.getNumber().intValue(),
                maxLayersSpinnerModel.getNumber().intValue());
        dispose();
    }
}
